﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Security.Principal;
using System.Text;
using System.Windows.Forms;

namespace UacHelper
{
    public class UacUtil
    {
        /// <summary>
        /// run current process as administrator
        /// </summary>
        public static void RunAsAdministrator()
        {
            if (IsRunAsAdmin())
                return;
            var arguments = new List<string>();
            var args = Environment.GetCommandLineArgs();
            for (int i = 1; i < args.Length; i++)
            {
                arguments.Add(args[i]);
            }
            var process = new Process();
            process.StartInfo = new ProcessStartInfo(Application.ExecutablePath, string.Join(" ", arguments.ToArray()));
            process.StartInfo.Verb = "runas";
            process.StartInfo.WorkingDirectory = Application.StartupPath;
            process.Start();
            Environment.Exit(0);
        }
        /// <summary>
        /// Sets the elevation required state for a specified button or command 
        /// link to display an elevated icon. 
        /// </summary>
        /// <param name="handle"></param>
        public static void SetShield(IntPtr handle)
        {
            var elevated = IsProcessElevated();
            var ctrl=Control.FromHandle(handle);
            if (ctrl is LinkLabel)
                (ctrl as LinkLabel).FlatStyle = FlatStyle.System;
            else if (ctrl is Button) {
                (ctrl as Button).FlatStyle = FlatStyle.System;
            }
            NativeMethods.SendMessage(handle, NativeMethods.BCM_SETSHIELD, 0, elevated ? IntPtr.Zero : new IntPtr(1));
        }
        /// <summary>
        /// The function checks whether the current process is run as administrator.
        /// In other words, it dictates whether the primary access token of the 
        /// process belongs to user account that is a member of the local 
        /// Administrators group and it is elevated.
        /// </summary>
        /// <returns>
        /// Returns True if the primary access token of the process belongs to user 
        /// account that is a member of the local Administrators group and it is 
        /// elevated. Returns False if the token does not.
        /// </returns>
        public static bool IsRunAsAdmin()
        {
            WindowsPrincipal principal = new WindowsPrincipal(WindowsIdentity.GetCurrent());
            return principal.IsInRole(WindowsBuiltInRole.Administrator);
        }
        /// <summary>
		/// The function checks whether the primary access token of the process belongs 
		/// to user account that is a member of the local Administrators group, even if 
		/// it currently is not elevated.
		/// </summary>
		/// <returns>
		/// Returns True if the primary access token of the process belongs to user 
		/// account that is a member of the local Administrators group. Returns False 
		/// if the token does not.
		/// </returns>
		/// <exception cref="T:System.ComponentModel.Win32Exception">
		/// When any native Windows API call fails, the function throws a Win32Exception 
		/// with the last error code.
		/// </exception>
        public static bool IsUserInAdminGroup()
        {
            bool fInAdminGroup = false;
            SafeTokenHandle hToken = null;
            SafeTokenHandle hTokenToCheck = null;
            IntPtr pElevationType = IntPtr.Zero;
            IntPtr pLinkedToken = IntPtr.Zero;
            int cbSize = 0;
            try
            {
                bool flag = !NativeMethods.OpenProcessToken(Process.GetCurrentProcess().Handle, 10u, out hToken);
                if (flag)
                {
                    throw new Win32Exception();
                }
                bool flag2 = Environment.OSVersion.Version.Major >= 6;
                if (flag2)
                {
                    cbSize = 4;
                    pElevationType = Marshal.AllocHGlobal(cbSize);
                    bool flag3 = pElevationType == IntPtr.Zero;
                    if (flag3)
                    {
                        throw new Win32Exception();
                    }
                    bool flag4 = !NativeMethods.GetTokenInformation(hToken, TOKEN_INFORMATION_CLASS.TokenElevationType, pElevationType, cbSize, out cbSize);
                    if (flag4)
                    {
                        throw new Win32Exception();
                    }
                    TOKEN_ELEVATION_TYPE elevType = (TOKEN_ELEVATION_TYPE)Marshal.ReadInt32(pElevationType);
                    bool flag5 = elevType == TOKEN_ELEVATION_TYPE.TokenElevationTypeLimited;
                    if (flag5)
                    {
                        cbSize = IntPtr.Size;
                        pLinkedToken = Marshal.AllocHGlobal(cbSize);
                        bool flag6 = pLinkedToken == IntPtr.Zero;
                        if (flag6)
                        {
                            throw new Win32Exception();
                        }
                        bool flag7 = !NativeMethods.GetTokenInformation(hToken, TOKEN_INFORMATION_CLASS.TokenLinkedToken, pLinkedToken, cbSize, out cbSize);
                        if (flag7)
                        {
                            throw new Win32Exception();
                        }
                        IntPtr hLinkedToken = Marshal.ReadIntPtr(pLinkedToken);
                        hTokenToCheck = new SafeTokenHandle(hLinkedToken);
                    }
                }
                bool flag8 = hTokenToCheck == null;
                if (flag8)
                {
                    bool flag9 = !NativeMethods.DuplicateToken(hToken, SECURITY_IMPERSONATION_LEVEL.SecurityIdentification, out hTokenToCheck);
                    if (flag9)
                    {
                        throw new Win32Exception();
                    }
                }
                WindowsIdentity id = new WindowsIdentity(hTokenToCheck.DangerousGetHandle());
                WindowsPrincipal principal = new WindowsPrincipal(id);
                fInAdminGroup = principal.IsInRole(WindowsBuiltInRole.Administrator);
            }
            finally
            {
                bool flag10 = hToken != null;
                if (flag10)
                {
                    hToken.Close();
                    hToken = null;
                }
                bool flag11 = hTokenToCheck != null;
                if (flag11)
                {
                    hTokenToCheck.Close();
                    hTokenToCheck = null;
                }
                bool flag12 = pElevationType != IntPtr.Zero;
                if (flag12)
                {
                    Marshal.FreeHGlobal(pElevationType);
                    pElevationType = IntPtr.Zero;
                }
                bool flag13 = pLinkedToken != IntPtr.Zero;
                if (flag13)
                {
                    Marshal.FreeHGlobal(pLinkedToken);
                    pLinkedToken = IntPtr.Zero;
                }
            }
            return fInAdminGroup;
        }

        /// <summary>
		/// The function gets the elevation information of the current process. It 
		/// dictates whether the process is elevated or not. Token elevation is only 
		/// available on Windows Vista and newer operating systems, thus 
		/// IsProcessElevated throws a C++ exception if it is called on systems prior 
		/// to Windows Vista. It is not appropriate to use this function to determine 
		/// whether a process is run as administartor.
		/// </summary>
		/// <returns>
		/// Returns True if the process is elevated. Returns False if it is not.
		/// </returns>
		/// <exception cref="T:System.ComponentModel.Win32Exception">
		/// When any native Windows API call fails, the function throws a Win32Exception
		/// with the last error code.
		/// </exception>
		/// <remarks>
		/// TOKEN_INFORMATION_CLASS provides TokenElevationType to check the elevation 
		/// type (TokenElevationTypeDefault / TokenElevationTypeLimited / 
		/// TokenElevationTypeFull) of the process. It is different from TokenElevation 
		/// in that, when UAC is turned off, elevation type always returns 
		/// TokenElevationTypeDefault even though the process is elevated (Integrity 
		/// Level == High). In other words, it is not safe to say if the process is 
		/// elevated based on elevation type. Instead, we should use TokenElevation.
		/// </remarks>
		public static bool IsProcessElevated()
        {
            bool fIsElevated = false;
            SafeTokenHandle hToken = null;
            int cbTokenElevation = 0;
            IntPtr pTokenElevation = IntPtr.Zero;
            try
            {
                bool flag = !NativeMethods.OpenProcessToken(Process.GetCurrentProcess().Handle, 8u, out hToken);
                if (flag)
                {
                    throw new Win32Exception();
                }
                cbTokenElevation = Marshal.SizeOf(typeof(TOKEN_ELEVATION));
                pTokenElevation = Marshal.AllocHGlobal(cbTokenElevation);
                bool flag2 = pTokenElevation == IntPtr.Zero;
                if (flag2)
                {
                    throw new Win32Exception();
                }
                bool flag3 = !NativeMethods.GetTokenInformation(hToken, TOKEN_INFORMATION_CLASS.TokenElevation, pTokenElevation, cbTokenElevation, out cbTokenElevation);
                if (flag3)
                {
                    throw new Win32Exception();
                }
                object expr_90 = Marshal.PtrToStructure(pTokenElevation, typeof(TOKEN_ELEVATION));
                TOKEN_ELEVATION elevation = (expr_90 != null) ? ((TOKEN_ELEVATION)expr_90) : default(TOKEN_ELEVATION);
                fIsElevated = (elevation.TokenIsElevated != 0);
            }
            finally
            {
                bool flag4 = hToken != null;
                if (flag4)
                {
                    hToken.Close();
                    hToken = null;
                }
                bool flag5 = pTokenElevation != IntPtr.Zero;
                if (flag5)
                {
                    Marshal.FreeHGlobal(pTokenElevation);
                    pTokenElevation = IntPtr.Zero;
                    cbTokenElevation = 0;
                }
            }
            return fIsElevated;
        }
        public static ProcessIntegrityLevel GetProcessIntegrityLevel(int level)
        {
            if (level <= 4096)
            {
                if (level == 0)
                {
                    return ProcessIntegrityLevel.Untrusted;
                }
                if (level == 4096)
                {
                    return ProcessIntegrityLevel.Low;
                }
            }
            else
            {
                if (level == 8192)
                {
                    return ProcessIntegrityLevel.Medium;
                }
                if (level == 12288)
                {
                    return ProcessIntegrityLevel.High;
                }
                if (level == 16384)
                {
                    return ProcessIntegrityLevel.System;
                }
            }
            return ProcessIntegrityLevel.Unknown;
        }
        /// <summary>
		/// The function gets the integrity level of the current process. Integrity 
		/// level is only available on Windows Vista and newer operating systems, thus 
		/// GetProcessIntegrityLevel throws a C++ exception if it is called on systems 
		/// prior to Windows Vista.
		/// </summary>
		/// <returns>
		/// Returns the integrity level of the current process. It is usually one of 
		/// these values:
		///
		///    SECURITY_MANDATORY_UNTRUSTED_RID - means untrusted level. It is used by 
		///    processes started by the Anonymous group. Blocks most write access.
		///    (SID: S-1-16-0x0)
		///
		///    SECURITY_MANDATORY_LOW_RID - means low integrity level. It is used by 
		///    Protected Mode Internet Explorer. Blocks write acess to most objects 
		///    (such as files and registry keys) on the system. (SID: S-1-16-0x1000)
		///
		///    SECURITY_MANDATORY_MEDIUM_RID - means medium integrity level. It is used 
		///    by normal applications being launched while UAC is enabled. 
		///    (SID: S-1-16-0x2000)
		///
		///    SECURITY_MANDATORY_HIGH_RID - means high integrity level. It is used by 
		///    administrative applications launched through elevation when UAC is 
		///    enabled, or normal applications if UAC is disabled and the user is an 
		///    administrator. (SID: S-1-16-0x3000)
		///
		///    SECURITY_MANDATORY_SYSTEM_RID - means system integrity level. It is used 
		///    by services and other system-level applications (such as Wininit, 
		///    Winlogon, Smss, etc.)  (SID: S-1-16-0x4000)
		///
		/// </returns>
		/// <exception cref="T:System.ComponentModel.Win32Exception">
		/// When any native Windows API call fails, the function throws a Win32Exception 
		/// with the last error code.
		/// </exception>
		public static int GetProcessIntegrityLevel()
        {
            int IL = -1;
            SafeTokenHandle hToken = null;
            int cbTokenIL = 0;
            IntPtr pTokenIL = IntPtr.Zero;
            try
            {
                bool flag = !NativeMethods.OpenProcessToken(Process.GetCurrentProcess().Handle, 8u, out hToken);
                if (flag)
                {
                    throw new Win32Exception();
                }
                bool flag2 = !NativeMethods.GetTokenInformation(hToken, TOKEN_INFORMATION_CLASS.TokenIntegrityLevel, IntPtr.Zero, 0, out cbTokenIL);
                if (flag2)
                {
                    int err = Marshal.GetLastWin32Error();
                    bool flag3 = err != 122;
                    if (flag3)
                    {
                        throw new Win32Exception(err);
                    }
                }
                pTokenIL = Marshal.AllocHGlobal(cbTokenIL);
                bool flag4 = pTokenIL == IntPtr.Zero;
                if (flag4)
                {
                    throw new Win32Exception();
                }
                bool flag5 = !NativeMethods.GetTokenInformation(hToken, TOKEN_INFORMATION_CLASS.TokenIntegrityLevel, pTokenIL, cbTokenIL, out cbTokenIL);
                if (flag5)
                {
                    throw new Win32Exception();
                }
                object expr_BA = Marshal.PtrToStructure(pTokenIL, typeof(TOKEN_MANDATORY_LABEL));
                TOKEN_MANDATORY_LABEL tokenIL = (expr_BA != null) ? ((TOKEN_MANDATORY_LABEL)expr_BA) : default(TOKEN_MANDATORY_LABEL);
                IntPtr pIL = NativeMethods.GetSidSubAuthority(tokenIL.Label.Sid, 0u);
                IL = Marshal.ReadInt32(pIL);
            }
            finally
            {
                bool flag6 = hToken != null;
                if (flag6)
                {
                    hToken.Close();
                    hToken = null;
                }
                bool flag7 = pTokenIL != IntPtr.Zero;
                if (flag7)
                {
                    Marshal.FreeHGlobal(pTokenIL);
                    pTokenIL = IntPtr.Zero;
                    cbTokenIL = 0;
                }
            }
            return IL;
        }
        public enum ProcessIntegrityLevel
        {
            Unknown,
            Untrusted,
            Low,
            Medium,
            High,
            System
        }
    }
}
